﻿#if !WINDOWS_PHONE
using System.Collections.Concurrent;
#else
using System.Collections.Generic;
#endif
using System.Diagnostics.Contracts;
#if WINDOWS_PHONE
using System.Threading;
#endif
using System.Threading.Tasks;

namespace DaveSexton.Labs
{
	internal sealed class AsyncTaskQueue
	{
#if !WINDOWS_PHONE
		private readonly ConcurrentQueue<Task> tasks = new ConcurrentQueue<Task>();
#else
		private readonly Queue<Task> tasks = new Queue<Task>();
		private readonly object gate = new object();
#endif

		public void Enqueue(Task task)
		{
			Contract.Requires(task != null);

#if !WINDOWS_PHONE
			tasks.Enqueue(task);
#else
			lock (gate)
			{
				tasks.Enqueue(task);
			}
#endif
		}

		public async Task AwaitAllAsync()
		{
			Contract.Ensures(Contract.Result<Task>() != null);

#if !WINDOWS_PHONE
			while (tasks.Count > 0)
			{
				Task task;
				if (tasks.TryDequeue(out task))
				{
					await task;
				}
			}
#else
			var lockTaken = false;
			try
			{
				Monitor.Enter(gate, ref lockTaken);

				Contract.Assume(lockTaken);

				while (tasks.Count > 0)
				{
					var task = tasks.Dequeue();

					if (lockTaken)
					{
						Monitor.Exit(gate);

						lockTaken = false;
					}

					await task;

					Monitor.Enter(gate, ref lockTaken);

					Contract.Assume(lockTaken);
				}
			}
			finally
			{
				if (lockTaken)
				{
					Monitor.Exit(gate);
				}
			}
#endif
		}
	}
}